/*
 * 1.自定义选项界面格式
 * 		自定义选项界面格式需通过customOptionComponent参数传入一个组件名称，如：'wt-anesthesiaName-item'，该组件请自行定义，一般在model中定义，
 * 2.自定义数据格式
 * 		有时为了更改点击选项后获取的值，如将'option1'改为{id:1,label:'option1'}，这时需要传入customResultParser，该字段为一个方法，接收一个数组参数（如：['李医生','赵医生']），返回定制化后的数组（如：[{id:1,name:'李医生'},{id:2,name:'赵医生'}]）
 * 3.自定义多选情况下，选中标签的显示内容
 * 		选中的标签会显示点击选项后返回的值，若使用了自定义数据格式，则很可能会显示为一个对象字符串，这时需要传入profixSelectedDisplayInUIFun方法，该方法用来从currentValue取值，返回['string1','string2'...]格式的值，作为显示的数据，建议从model里配置。
 */
<template>
	<div class="wt-autocomplete">
		<template v-if="multiple">
			<el-tag v-for="(item,index) in selectedDisplayInUI" :key="item" :closable="true" type="primary" :hit="true" @close="handleTagClose(index)">{{ item }}</el-tag>
		</template>
		<el-alert
			v-if="allowAdd && config && config.allowAdd"
			:class="{'wt-alert-with-button':allowUseWithoutAdd}"
			:title="notice" 
			:type="added ? 'success' : 'info'"
			show-icon
			:closable="true"
			style="margin-bottom:5px"></el-alert>
		<template v-if="multiple">
			<el-autocomplete
			ref="input"
			:class="{'wt-alert-with-button':allowUseWithoutAdd}"
			:disabled="disabled"
			v-model="inputValue"
			:fetch-suggestions="querySearchAsync"
			:placeholder="placeholder"
			@select="handleSelect"
			:trigger-on-focus="triggerOnFocus"
			:custom-item="customOptionComponent"
			:icon="icon"
			:on-icon-click="handleIconClick"
			@keyup.enter.native="handleIconClick"
			></el-autocomplete>
		</template>
		<template v-else>
			<el-autocomplete
			ref="input"
			:disabled="disabled"
			v-model="currentValue"
			:fetch-suggestions="querySearchAsync"
			:placeholder="placeholder"
			@input.native="handleInput($event,currentValue)"
			@select="handleSelect"
			:trigger-on-focus="triggerOnFocus"
			:custom-item="customOptionComponent"
			:icon="icon"
			:on-icon-click="handleIconClick"
			@keyup.enter.native="handleIconClick"
			></el-autocomplete>
		</template>
		<el-button v-if="enableUseWithoutAdd" @click="useWithouAdd">不收录，直接使用</el-button>
	</div>
</template>
<script>
/**
	 * 封装了el-autocomplete组件，实现输入时自动查询
			Props:
				multi-select 是否允许多选
				value 如果单选，为字符串值，如果多选，则为一个数组。
				allow-add 当输入的内容在服务器上查询不到时，是否允许添加
				placeholder 没有输入时显示的提示信息
				item-query-url: 用于查询的url，该url应该返回一个json数组
				item-query-param: 用于查询的关键字参数名
				item-query-pagination: 查询接口是否要求分页信息
				item-query-result-param: 查询结果集中的参数名
			Events:
				select 用户从建议列表中选择了一项数据时触发，参数为data - 查询所得的数据
				change 用户选择
			Known bugs：
				custom-optioin-info以及option-info-param参数不起作用
	 */
import Vue from "vue";
import ElementUI from "element-ui";
import ModelUtils from "../utils/wt-model-utils";
import request from "../utils/axios";
import utils from "../utils/utils";

Vue.use(ElementUI);

const defaultNotice =
	'输入关键字进行查找，如果该关键词不存在，可点击"+"按钮收录到数据库';

export default {
	name: "wt-autocomplete",
	props: {
		value: [String, Array],
		multiple: {
			type: Boolean,
			default: false
		},
		allowAdd: {
			type: Boolean,
			default: true
		},
		placeholder: {
			type: String,
			default: "请输入关键字进行查询"
		},
		customOptionInfo: {
			type: Function,
			default: function(item) {
				// 默认不显示额外信息
				return "";
			}
		},
		customOptionComponent: {
			type: String,
			default: "wt-autocomplete-item"
		},
		optionInfoParam: {
			type: String,
			default: ""
		},
		query: {
			type: Object,
			default: function() {
				return {
					// 查询的url
					url: "",
					// 用于查询的参数名
					filterParams: "",
					allowEmptyQuery: false,
					// 查询调用的http method
					method: "GET",
					// 是否是一个中转请求
					transfer: true,
					// 是否添加分页参数
					pagination: false,
					// 结果集中由于获取value的字段名
					valueParam: ""
				};
			}
		},
		triggerOnFocus: {
			type: Boolean,
			default: true
		},
		paramsData: {
			type: Object,
			default: null
		},
		customIcon: {
			type: String,
			default: ""
		},
		customResultParser: {
			type: Function,
			default: undefined
		},
		autoSelect: {
			type: Boolean,
			default: false
		},
		disabled: {
			type: Boolean,
			default: false
		},
		allowUseWithoutAdd: {
			type: Boolean,
			default: false
		},
		// 仅用于多选时，是否允许选择重复项
		allowDuplicate: {
			type: Boolean,
			default: false
		},
		//多选时，用来将currentValue序列化成['string1','string2'...]的格式，作为显示的数据，需从model里配置各自的解析方式。曾直接使用currentValue，但currentValue格式多变，不适用
		profixSelectedDisplayInUIFun: {
			type: Function,
			default: function(data) {
				return typeof data == "string" ? JSON.parse(data) : data;
			}
		},
		selectValuefield: {
			type: String,
			default: null
		}
	},
	data() {
		return {
			currentValue: this.value,
			inputValue: "",
			// 用于记录当前搜索结果的keyword
			currentKeyword: null,
			// 刚刚添加过的关键词
			lastAddedKeyword: null,
			config: this.query,
			// 是否正在添加关键词
			adding: false,
			// 标记当前关键词是否不存在于列表中（可以被添加收录）
			noItemFound: false,
			// 当前关键词是否已被成功收录
			added: false,
			//缓存通过关键字查询的结果
			searchCache: {}
		};
	},
	watch: {
		value(val, oldValue) {
			this.setCurrentValue(val);
		}
	},
	computed: {
		selectedDisplayInUI() {
			//profixSelectedDisplayInUIFun方法需要在model中定义，用来将currentValue序列化成['string1','string2'...]的格式,如：currentValue格式为[{id:5,name:'demo1'}]格式时
			if (this.currentValue)
				return this.profixSelectedDisplayInUIFun(this.currentValue);
		},
		useCache() {
			return this.config && !this.config.disableCache;
		},
		canAdd: function() {
			return (
				this.allowAdd &&
				this.config &&
				this.config.allowAdd &&
				this.noItemFound &&
				this.currentInput != null &&
				this.currentInput != ""
			);
		},
		isLoading() {
			return this.$refs.input.loading;
		},
		notice: function() {
			return this.canAdd
				? '点击"+"按钮将"' + this.currentInput + '"添加到数据库'
				: this.added
					? '已将"' + this.lastAddedKeyword + '" 收录到数据库'
					: defaultNotice;
		},
		icon: function() {
			return this.canAdd
				? this.adding ? "loading" : "plus"
				: this.customIcon;
		},
		currentInput: function() {
			let val = this.multiple ? this.inputValue : this.currentValue;
			return val;
		},
		itemQueryUrl() {
			return this.query.url;
		},
		itemQueryPagination() {
			return this.query.pagination;
		},
		itemQueryResultParam() {
			return this.query.value;
		},
		// 是否显示不收录直接使用按钮
		enableUseWithoutAdd() {
			return (
				this.allowUseWithoutAdd &&
				this.canAdd &&
				this.multiple &&
				!this.adding
			);
		}
	},
	methods: {
		setCurrentValue(value) {
			if (value === this.currentValue) return;
			this.currentValue = value;
		},
		setCurrentValueAndRaiseEvent(value) {
			this.setCurrentValue(value);
			this.$emit("input", JSON.stringify(value));
		},
		setItem(item) {
			//item = typeof(item) == 'string'?item:''+item
			if (this.multiple) {
				if (this.currentValue && typeof this.currentValue == "string") {
					this.currentValue = JSON.parse(this.currentValue);
				}
				this.currentValue = this.currentValue || [];
				if (!this.allowDuplicate) {
					// 检查重复
					for (let i = 0; i < this.currentValue.length; i++) {
						let currentValue = this.currentValue;
						if (utils.isEqu(currentValue[i], item)) {
							this.$message.warning(
								"已选择“" +
									(typeof item == "string"
										? item
										: this.selectedDisplayInUI[
												utils.indexOf(
													this.currentValue,
													item
												)
											]) +
									"”，请勿重复选择！"
							);
							this.inputValue = "";
							return;
						}
					}
				}
				this.currentValue.push(item);
			} else {
				this.currentValue = item;
			}
			this.$emit("input", this.currentValue);
			this.inputValue = "";
			this.canAdd = false;
		},
		handleInput(e, val) {
			this.$emit("input", val);
		},
		handleIconClick() {
			if (!this.canAdd) {
				this.$emit("custom-icon");
				return;
			}
			if (this.currentInput != "") {
				let vm = this;
				let addingKeyword = this.currentInput;
				let data = {};
				// if(this.customResultParser){
				// 	addingKeyword = JSON.stringify({id:0,[this.selectValuefield]:this.currentInput})
				// }
				data[this.config.addParam] = addingKeyword;
				ModelUtils.buildQueryExtraParams(
					this.config,
					this.paramsData,
					data
				);
				this.adding = true;
				request(
					this.config.url,
					this.config.addMethod,
					data,
					this.config.transfer,
					function(res) {
						vm.adding = false;
						if (typeof res == "string") {
							res = JSON.parse(res);
						}
						vm.setItem(addingKeyword);
						vm.$emit("select", res);
						vm.noItemFound = false;
						vm.added = true;
						vm.lastAddedKeyword = addingKeyword;
						vm.clearCache(addingKeyword);
					},
					function(error) {
						vm.adding = false;
						console.log(error);
						vm.$message.error("添加选项失败，请稍后重试");
					}
				);
			}
		},
		showQueryError(callback) {
			if (callback !== undefined) {
				callback([]);
				this.$message.error("查询出错，请稍后重试！");
			}
		},
		clearCache(keyword) {
			if (this.searchCache[keyword]) {
				this.searchCache[keyword] = null;
			}
		},
		setQueryResult(keyword, data, callback) {
			//searchCache，缓存搜索结果
			this.useCache &&
				!this.searchCache[keyword] &&
				(this.searchCache[keyword] = data);
			if (typeof data == "string") {
				data = JSON.parse(data).content;
			}
			let display = [];
			let foundItem = false;
			let autoSelectItem = null;
			let vm = this;
			if (this.customResultParser) {
				display = this.customResultParser(data);
			} else {
				data.forEach(function(value) {
					let item = {
						value: value.patientInfo.name,
						data: value[vm.itemQueryResultParam]
					};
					console.log("item", item);
					if (value[vm.itemQueryResultParam] == vm.currentInput) {
						foundItem = true;
						if (vm.autoSelect) {
							autoSelectItem = item;
						}
					}
					display.push(item);
				});
				this.noItemFound = !foundItem;
			}
			callback(display);
			if (autoSelectItem != null) {
				this.handleSelect(autoSelectItem);
			}
		},
		querySearchAsync(queryString, callback) {
			let vm = this;
			let keyword = queryString;
			console.log("keyWord", keyword);
			if (this.searchCache[keyword]) {
				//读取缓存的搜索结果
				let data = this.searchCache[keyword];
				return vm.setQueryResult(keyword, data, callback);
			}
			if (
				(keyword == "" || keyword == null) &&
				!this.query.allowEmptyQuery
			) {
				callback([]);
			}

			let path = this.itemQueryUrl;
			let data = null;
			if (this.query.filterParams) {
				data = {
					tagName: this.query.filterParams,
					tagRegex: keyword ? keyword : "",
					role: "patient"
				};
				if (this.itemQueryPagination) {
					data.pageIndex = 0;
					data.pageSize = 500;
				}
				ModelUtils.buildQueryExtraParams(
					this.query,
					this.paramsData,
					data
				);
			} else {
				path = path + keyword;
			}
			let calling = null;
			if (data != null) {
				calling = window.axios.wtCall(path, "GET", data);
			} else {
				calling = window.axios.wtCall(path);
			}
			this.added = false;
			this.noItemFound = false;
			calling
				.then(function(response) {
					// 如果关键字已经变化，则忽略该查询结果
					if (keyword != vm.currentInput) {
						return;
					}
					if (response.status == 200 && response.data.status == 200) {
						let data = response.data.content;
						vm.setQueryResult(keyword, data, callback);
					} else {
						vm.showQueryError(callback);
					}
				})
				.catch(function(error) {
					// 如果关键字已经变化，则忽略该查询结果
					if (keyword != vm.currentInput) {
						return;
					}
					console.error(error);
					vm.showQueryError(callback);
				});
		},
		handleSelect(item) {
			this.noItemFound = false;
			this.setItem(item.value || item);
			console.log("selecting", item);
			this.$emit("select", item.data);
		},
		handleTagClose(index) {
			var items = this.currentValue;
			console.log("close", items);
			items.splice(index, 1);
			this.setCurrentValueAndRaiseEvent(items);
			// return;
			// for (var i = 0; i < items.length; i ++) {
			// 	if(utils.isEqu(items[i],item)){
			// 		items.splice(i, 1)
			// 		this.setCurrentValueAndRaiseEvent(items);
			// 		break;
			// 	}
			// }
		},
		useWithouAdd() {
			if (this.customResultParser) {
				this.setItem({
					id: 0,
					[this.selectValuefield]: this.currentInput
				});
			} else {
				this.setItem(this.currentInput);
			}
			this.noItemFound = false;
		}
	},
	mounted() {
		let infoDisplay = this.customOptionInfo;
		let infoParam = this.optionInfoParam;
		if (this.customOptionComponent == "wt-autocomplete-item") {
			Vue.component(this.customOptionComponent, {
				functional: true,
				render: function(h, ctx) {
					var item = ctx.props.item;
					var info = "";
					if (infoParam != "") {
						info = item.data[infoParam];
					} else {
						info = infoDisplay(item.data);
					}
					let display = [
						h("div", { attrs: { class: "name" } }, [item.value])
					];
					if (info != "") {
						display.push(
							h("span", { attrs: { class: "info" } }, [info])
						);
					}
					return h("li", ctx.data, display);
				},
				props: {
					item: { type: Object, required: true }
				}
			});
		}
	}
};
</script>

<style>
.wt-autocomplete .el-tag {
	margin-right: 10px;
}
.wt-autocomplete .wt-alert-with-button {
	width: 75%;
}
.wt-autocomplete .wt-input-with-button {
	display: inline-block;
}
.wt-autocomplete button {
	margin-top: 10px;
}
</style>
